using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Microsoft.SmallBasic.Library;
using Microsoft.SmallBasic.Library.Internal;

namespace SmallBasicFun
{
	public static class Helper
	{
		public static T GetField<T>(Type t, string name)
		{
			var flags = BindingFlags.Static | BindingFlags.NonPublic;
			var field = t.GetField(name, flags);
			return (T) field.GetValue(null);
		}

		public static T GetProperty<T>(Type t, string name)
		{
			var flags = BindingFlags.Static | BindingFlags.NonPublic;
			var p = t.GetProperties(flags);
			var methodInfo = p.Where(g => g.Name.Equals(name)).FirstOrDefault();
			return (T) methodInfo.GetValue(null, null);
		}

		public static T GetMethod<T>(Type t, string name, params object[] parameters)
		{
			var flags = BindingFlags.Static | BindingFlags.NonPublic;

			var field = t.GetMethods(flags).Where(m => m.Name.Equals(name)).FirstOrDefault();
			return (T) field.Invoke(null, parameters);
		}
	}

	public static class GraphicsWindowHelper
	{
		public static void Invoke(InvokeHelper action)
		{
			Helper.GetMethod<Window>(typeof (GraphicsWindow), "Invoke", action);
		}

		public static void AddShape(string name, FrameworkElement ellipse)
		{
			var flags = BindingFlags.Static | BindingFlags.NonPublic;
			var t = typeof (GraphicsWindow);
			var field = t.GetMethod("AddShape", flags);
			var window = (Window) field.Invoke(null, new object[] {name, ellipse});
		}

		public static Color GetColorFromString(String primitive)
		{
			var flags = BindingFlags.Static | BindingFlags.NonPublic;
			var t = typeof (GraphicsWindow);
			var field = t.GetMethod("GetColorFromString", flags);
			var color = (Color) field.Invoke(null, new object[] {primitive});
			return color;
		}

		public static Canvas GetCanvas()
		{
			return Helper.GetField<Canvas>(typeof (GraphicsWindow), "_mainCanvas");
		}

		public static Application GetApplication()
		{
			return Helper.GetField<Application>(typeof (SmallBasicApplication), "_application");
		}

		public static Window GetWindow()
		{
			return Helper.GetField<Window>(typeof (GraphicsWindow), "_window");
		}

		public static T InvokeWithReturn<T>(Func<T> func)
		{
			return (T) GetDispatcher().Invoke(DispatcherPriority.Render, func);
		}

		private static Dispatcher GetDispatcher()
		{
			return Helper.GetField<Dispatcher>(typeof (SmallBasicApplication), "_dispatcher");
		}

		public static void AddClosingEventFirst(CancelEventHandler action)
		{
			var window = GetWindow();
			var events =
				(EventHandlerList)
				window.GetType().GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(window, null);
			var key = window.GetType().GetField("EVENT_CLOSING", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
			events[key] = action + (CancelEventHandler) events[key];
		}

		public static void ResetTurtle()
		{
			Tortoise.Reset();
			GiantTortoise.Reset();
		}

		public static void AddClosingEventFirstToApplicationClose(ExitEventHandler action)
		{
			Invoke(() => GetApplication().Exit += action);
		}

		public static void ResetGraphicsWindow()
		{
			var flags = BindingFlags.Static | BindingFlags.NonPublic;
			var type = typeof (GraphicsWindow);
			var constructor = type.GetConstructor(flags, null, new Type[0], null);
			constructor.Invoke(null, null);
		}

		public static void PlaceImageAt(Uri uri, int y, int x, Canvas canvas)
		{
			Invoke(() =>
			       	{
			       		try
			       		{
			       			ImageSource source = new BitmapImage(uri);
			       			var tower = new Image
			       			            	{
			       			            		Source = source,
			       			            		Margin = new Thickness(-8.0, -8.0, 0.0, 0.0),
			       			            		Height = source.Height*5,
			       			            		Width = source.Width*5
			       			            	};

			       			Canvas.SetTop(tower, y);
			       			Canvas.SetLeft(tower, x);
			       			canvas.Children.Add(tower);
			       		}
			       		catch (Exception e)
			       		{
			       			Debug.Print(e.Message);
			       		}
			       	});
		}

		public static Pen GetPen()
		{
			return Helper.GetField<Pen>(typeof (GraphicsWindow), "_pen");
		}

		public static void VerifyAccess()
		{
			Helper.GetMethod<object>(typeof (GraphicsWindow), "VerifyAccess");
		}
	}

	public class ShapesHelper
	{
		public static string GenerateNewName(string name)
		{
			return Helper.GetMethod<string>(typeof (Shapes), "GenerateNewName", name);
		}
	}

	public class SmallBasicApplicationHelper
	{
		public static Uri GetResourceUri(string name)
		{
			return Helper.GetMethod<Uri>(typeof (SmallBasicApplication), "GetResourceUri", name);
		}

		public static bool HasShutdown()
		{
			return Helper.GetProperty<bool>(typeof (SmallBasicApplication), "HasShutdown");
		}

		public static void Invoke(InvokeHelper action)
		{
			Helper.GetMethod<Window>(typeof (SmallBasicApplication), "Invoke", action);
		}

		public static Dispatcher Dispatcher()
		{
			return Helper.GetProperty<Dispatcher>(typeof (SmallBasicApplication), "Dispatcher");
		}

		public static void ClearDispatcherQueue()
		{
			Helper.GetMethod<object>(typeof (SmallBasicApplication), "ClearDispatcherQueue");
		}
	}
}